class SearchController < ApplicationController
  def sub_layout
    "search"
  end

  def new
    # collect history
    @history = collect_history

    # collect bookmarked items
    @bookmarked = collect_bookmarked
  end

  def search
    # get parameters
    search_string = params[:q] || ""
    search_string = search_string .length > 255 ? search_string [1..255] : search_string
    option_string = params[:o] || ""

    # create tokens
    @query = Hash.new
    search_tokens = collect_tokens(search_string)
    @query[:search] = search_tokens
    @query[:naked] = Hash.new
    @query[:naked][:query] = "#{search_tokens[:basic].join(' ')}"
    @query[:naked][:query] += " all(#{search_tokens[:advanced]['all'].join(' ')})"
    @query[:naked][:query] += " phrase(#{search_tokens[:advanced]['phrase'].join(' ')})"
    @query[:naked][:query] += " exclude(#{search_tokens[:advanced]['exclude'].join(' ')})"
    @query[:naked][:query] += " author(#{search_tokens[:advanced]['author'].join(' ')})"
    option_tokens = string_to_options(option_string)
    option_tokens['type1'] = (option_tokens['type1'][0] == 'true') ? true : false
    option_tokens['type2'] = (option_tokens['type2'][0] == 'true') ? true : false
    option_tokens['type3'] = (option_tokens['type3'][0] == 'true') ? true : false
    option_tokens['type4'] = (option_tokens['type4'][0] == 'true') ? true : false
    option_tokens['type5'] = (option_tokens['type5'][0] == 'true') ? true : false
    @query[:option] = option_tokens
    @query[:naked][:options] = option_string

    # save parameters in history
    if !Resource::History.exists?(:query => @query[:naked][:query])
      history = Resource::History.new(:query => @query[:naked][:query])
      history.save
    end
    #TODO: needs eval on exdistance of string

    # collect results
    results = collect_results(search_tokens, option_tokens)

    # order the results by score (default)
    results = order_results_score(results, search_tokens, option_tokens)
    @results = results

    # collect history
    @history = collect_history

    # collect bookmarked items
    @bookmarked = collect_bookmarked

    # page vars
    @page_nr = params[:p] || 1
    @page_limit = 10;
  end

  def preview_item
    item_id = params[:id] || ""
    @empty = (params[:e].nil?) ? false : true
    @item = Resource::Item.find(item_id)

    render :partial => 'resource/items/preview_item'
  end

  def suggestion_box
    @suggest_query = params[:suggestionQuery] || ""
    @suggest_query.gsub!(/[()]/, ' ')
    @suggest_query = collect_tokens(@suggest_query)
    @suggestions = @suggest_query[:basic].collect { |token|
      Resource::Token.find(:all, :conditions => ['content LIKE ?', "#{token}%"])
    }.flatten.uniq
    @synonims = Array.new
    @suggestions.each do |token|
      @synonims << token.find_synonims
    end
    @synonims.flatten!.uniq! unless @synonims.empty?

    render :partial => 'suggestion_box'
  end

  def delete_history
    id = params[:id].to_i || nil
    if id.nil?
      render :text => 'something went wrong, nothing happened'
    else
      Resource::History.destroy(id)
      render :text => '<em>history removed</em><script type="text/javascript">setTimeout("jQuery(\'#hist-' + id.to_s + '\').hide()", 1500);</script>'
    end
  end

  def un_bookmark
    id = params[:id].to_i || nil
    if id.nil?
      render :text => 'something went wrong, nothing happened'
    else
      item = Resource::Item.find(id)
      item.update_attribute(:bookmarked, !item.bookmarked)
      render :text => '<em>bookmark unmarked</em><script type="text/javascript">setTimeout("jQuery(\'#bookm-' + id.to_s + '\').hide()", 1500);</script>'
    end
  end

  # should be private

  def collect_tokens(string)
    result = Hash.new
    string.downcase!
    return [] if string == nil
    # check if string has advanced tokens
    if string.include? '('
      first_type = ''
      if (string.index('all(') < string.index('phrase(')) && (string.index('all(') < string.index('exclude(')) && (string.index('all(') < string.index('author('))
        split_string = string.split('all(',2)
        first_type = 'all('
      elsif (string.index('phrase(') < string.index('all(')) && (string.index('phrase(') < string.index('exclude(')) && (string.index('phrase(') < string.index('author('))
        split_string = string.split('phrase(',2)
        first_type = 'phrase('
      elsif (string.index('exclude(') < string.index('all(')) && (string.index('exclude(') < string.index('phrase(')) && (string.index('exclude(') < string.index('author('))
        split_string = string.split('exclude(',2)
        first_type = 'exclude('
      elsif (string.index('author(') < string.index('all(')) && (string.index('author(') < string.index('phrase(')) && (string.index('author(') < string.index('exclude('))
        split_string = string.split('author(',2)
        first_type = 'author('
      else
        split_string = string.split('all(',2)
        first_type = 'all('
      end
      # basic tokens
      result[:basic] = split_string[0].split(/ /).delete_if {|t| t.strip == ""}.uniq

      # isolate advanced tokens and collect them
      split_string -= [split_string[0]]
      split_string.collect! { |x| first_type + x }
      option_string = split_string.join('')
      result[:advanced] = string_to_options(option_string)
    else
      # basic tokens
      result[:basic] = string.split(/ /).delete_if {|t| t.strip == ""}.uniq
    end
    return result
  end

  def string_to_options(string)
    cats = Array.new
    result = Hash.new
    arr = string.split(')')
    arr.collect! { |x| x.split('(', 2) }
    arr.each { |x| cats << x[0].strip }
    cats.uniq!
    cats.each { |x| result[x] = ''}
    arr.each { |x| result[x[0].strip] += ' ' + x[1] if (x[1].length > 0) }
    result.each_key { |key| result[key].strip! }
    result.each_key { |key| result[key] = result[key].split(/ /).delete_if {|t| t.strip == ""}.uniq }

    return result
  end

  def collect_results(search, option)
    result = search[:basic].collect { |token|
      Resource::Item.find(:all, :conditions => ['title LIKE ? OR abstract LIKE ?', "%#{token}%", "%#{token}%"])
    }.flatten.uniq
    
    result.collect!{|res| {:score => 0, :object => res}}

    return result
  end

  def order_results_score(results, search, option)
    # score calculation by the ammount of tokens in result/ammount of words
    # title is 60% and abstract is 40% of score
    tmp_array = Array.new

    results.collect! { |result|

      title_count = 0
      abstract_count = 0
      
      search[:basic].each do |token|
        title_count += result[:object].title.downcase.scan(/#{token}/).length
        abstract_count += result[:object].abstract.downcase.scan(/#{token}/).length
      end

      result[:score] += 60*(title_count/(result[:object].title.scan(/ /).length.to_f+1))
      result[:score] += 40*(abstract_count/(result[:object].abstract.scan(/ /).length.to_f+1))
      tmp_array << result
    }

    # order the results by score, reverse put them from high to low score
    tmp_array = tmp_array.sort_by{ |r| r[:score]}.reverse
    
    return tmp_array
  end

  def collect_history
    Resource::History.find(:all)
  end

  def collect_bookmarked
    Resource::Item.find(:all, :conditions => {:bookmarked => true})
  end
end
